#pragma once
#include "../Util/util.hpp"
#include "db.hpp"
#include "matcher.hpp"
#include "online.hpp"
#include "room.hpp"
#include "session.hpp"

class gobang_server
{
private:
    std::string _web_root;
    wsserver_t _wsvr;
    user_table _ut;
    online_manager _om;
    room_manage _rm;
    session_manager _sm;
    matcher _mt;
private:
    // 1 静态资源请求处理
    void file_html(wsserver_t::connection_ptr conn)
    {
        // 获取资源
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        // 拼接实际路径
        std::string realpath = _web_root + uri;
        // 如果是目录要加后缀
        if(realpath.back() == '/')
        {
            realpath += "login.html";
        }
        // 读取文件内容
        std::string body;
        bool ret = File_Util::ReadFile(realpath, body);
        if(!ret)
        {
            body += "<html><body><h1> 404 </h1></body></html>";
            conn->set_status(websocketpp::http::status_code::not_found);
            conn->set_body(body);
            return;
        }
        // 发送响应正文
        conn->set_status(websocketpp::http::status_code::ok);
        conn->set_body(body);
    }
        // 共用函数
    void http_resp(wsserver_t::connection_ptr conn, bool result, 
    const std::string& reason, websocketpp::http::status_code::value code)
    {
        Json::Value json_resp;
        json_resp["result"] = result;
        json_resp["reason"] = reason;
        std::string body;
        // 序列化
        Json_Util::serializa(json_resp, body);
        conn->set_status(code);
        conn->set_body(body);
        conn->append_header("Content-Type", "application/json");
        return;
    }
    // 2 注册请求处理
    void reg(wsserver_t::connection_ptr conn)
    {
        websocketpp::http::parser::request req = conn->get_request();
        // 请求正文
        std::string req_body = conn->get_request_body();
        // 反序列化获取用户名和密码
        Json::Value json_req;
        bool ret = Json_Util::unserializa(req_body, json_req);
        if(!ret) 
        {
            LOG(ERROR) << "反序列化的格式错误" << "\n";
            return http_resp(conn, false, "请求格式错误", websocketpp::http::status_code::bad_request);
        }
        // 数据库增加用户
        if(json_req["username"].isNull() || json_req["password"].isNull())
        {
            LOG("ERROR") << "请输入用户名或密码" << "\n";
            return http_resp(conn, false, "请输入用户名或密码", websocketpp::http::status_code::bad_request); 
        }
        ret = _ut.insert(json_req);
        if(!ret)
        {
            LOG(ERROR) << "用户注册失败" << "\n";
            return http_resp(conn, false, "该用户已经被占用", websocketpp::http::status_code::bad_request); 
        }
        // 成功返回200
        return http_resp(conn, true, "注册成功", websocketpp::http::status_code::ok);  
    }
    // 3 登录请求处理
    void login(wsserver_t::connection_ptr conn)
    {
        // 请求正文
        std::string req_body = conn->get_request_body();
        // 反序列化
        Json::Value json_req;
        bool ret = Json_Util::unserializa(req_body, json_req);
        if(!ret) 
        {
            LOG(ERROR) << "反序列化的格式错误" << "\n";
            return http_resp(conn, false, "请求格式错误", websocketpp::http::status_code::bad_request);
        }
        // 数据库验证用户信息
        if(json_req["username"].isNull() || json_req["password"].isNull())
        {
            LOG("ERROR") << "请输入用户名或密码" << "\n";
            return http_resp(conn, false, "请输入用户名或密码", websocketpp::http::status_code::bad_request); 
        }
        ret = _ut.login(json_req);
        if(!ret)
        {
            LOG(ERROR) << "用户名或密码错误" << "\n";
            return http_resp(conn, false, "用户名或密码错误", websocketpp::http::status_code::bad_request); 
        }
        // 创建session
        uint64_t uid = json_req["id"].asUInt64();
        session_ptr sp = _sm.create_session(uid, LOGIN);
        if(sp.get() == nullptr)
        {
            LOG(ERROR) << "创建会话失败" << "\n";
            return http_resp(conn, false, "创建会话失败", websocketpp::http::status_code::internal_server_error); 
        }
        _sm.set_session_expire_time(sp->get_ssid(), FIXTIME);
        // 设置响应头部
        std::string cookie_ssid = "SSID=" + std::to_string(sp->get_ssid());
        conn->append_header("Set-Cookie", cookie_ssid);
        return http_resp(conn, true, "登录成功", websocketpp::http::status_code::ok); 
    }
        // cookie串分割 
    bool get_cookie_val(const std::string& cookie_str, const std::string& key, std::string& val)
    {
        std::vector<std::string> v1;
        std::string sep1 = ", ";
        Split_Util::splitStr(cookie_str, &v1, sep1);
        for(auto& substr : v1)
        {
            std::vector<std::string> v2;
            std::string sep2 = "=";
            Split_Util::splitStr(cookie_str, &v2, sep2);
            if(v2.size() != 2) continue;
            if(v2[0] == key)
            {
                val = v2[1];
                return true;
            }
        }
        return false;
    }
    // 4 获取信息请求处理
    void info(wsserver_t::connection_ptr conn)
    {
        // 获取请求信息中的cookie，如果没有就要重新登录
        std::string cookie_str = conn->get_request_header("Cookie");
        if(cookie_str.empty())
        {
            LOG(ERROR) << "找不到cookie" << "\n";
            return http_resp(conn, false, "找不到cookie", websocketpp::http::status_code::bad_request); 
        }
        // 找ssid
        std::string ssid_str;
        bool ret = get_cookie_val(cookie_str, "SSID", ssid_str);
        if(!ret)
        {
            LOG(ERROR) << "找不到ssid" << "\n";
            return http_resp(conn, false, "找不到ssid", websocketpp::http::status_code::bad_request); 
        }
        // 在session管理中查找对应的会话信息，如果没有则会话过期，重新登录
        session_ptr sp = _sm.get_session_by_ssid(std::stol(ssid_str));
        if(sp.get() == nullptr)
        {
            LOG(ERROR) << "找不到会话信息" << "\n";
            return http_resp(conn, false, "找不到会话信息", websocketpp::http::status_code::bad_request);
        }
        // 从数据库中取出用户信息，序列化后发送给客户端
        Json::Value user;
        uint64_t uid = sp->get_user();
        ret = _ut.sel_by_id(uid, user);
        if(!ret)
        {
            LOG(ERROR) << "获取用户信息失败" << "\n";
            return http_resp(conn, false, "获取用户信息失败", websocketpp::http::status_code::bad_request);
        }
        std::string json_str;
        Json_Util::serializa(user, json_str);
        conn->set_status(websocketpp::http::status_code::ok);
        conn->append_header("Content-Type", "application/json");
        conn->set_body(json_str);
        // 刷新session过期时间
        _sm.set_session_expire_time(sp->get_ssid(), FIXTIME);
    }
    // http回调处理函数
    void http_callback(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string method = req.get_method();
        std::string uri = req.get_uri();
        
        if(method == "POST" && uri == "/reg")
        {
            return reg(conn);
        }
        else if(method == "POST" && uri == "/login")
        {
            return login(conn);
        }
        else if(method == "GET" && uri == "/info")
        {
            return info(conn);
        }
        else 
        {
            return file_html(conn);
        }
    }

    // 获取会话信息
    session_ptr get_session_comm(wsserver_t::connection_ptr conn)
    {
        Json::Value err_resp;
        // 获取请求信息中的cookie，如果没有就要重新登录
        std::string cookie_str = conn->get_request_header("Cookie");
        if(cookie_str.empty())
        {
            err_resp["optype"] = "hall_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "找不到cookie信息, 重新登录";
            std::string body;
            Json_Util::serializa(err_resp, body);
            conn->send(body);
            return session_ptr();
        }
        // 找ssid
        std::string ssid_str;
        bool ret = get_cookie_val(cookie_str, "SSID", ssid_str);
        if(!ret)
        {
            err_resp["optype"] = "hall_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "找不到ssid信息, 重新登录";
            std::string body;
            Json_Util::serializa(err_resp, body);
            conn->send(body);
            return session_ptr();
        }
        // 在session管理中查找对应的会话信息，如果没有则会话过期，重新登录
        session_ptr sp = _sm.get_session_by_ssid(std::stol(ssid_str));
        if(sp.get() == nullptr)
        {
            err_resp["optype"] = "hall_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "找不到session会话信息, 重新登录";
            std::string body;
            Json_Util::serializa(err_resp, body);
            conn->send(body);
            return session_ptr();
        }
        return sp;
    }

    // 游戏大厅长连接建立
    void wsopen_game_hall(wsserver_t::connection_ptr conn)
    {
        // 验证登录 - 获取session信息
        session_ptr sp = get_session_comm(conn);
        if(sp.get() == nullptr)
        {
            return;
        }
        // 判断是否重复登录
        Json::Value err_resp;
        if(_om.is_in_game_hall(sp->get_user()) && _om.is_in_game_room(sp->get_user()))
        {
            err_resp["optype"] = "hall_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "客户端重复登录";
            std::string body;
            Json_Util::serializa(err_resp, body);
            conn->send(body);
            return;
        }
        // 客户端建立长连接加入游戏大厅
        _om.enter_game_hall(sp->get_user(), conn);
        // 客户端响应发送连接成功消息
        Json::Value json_resp;
        json_resp["optype"] = "hall_ready";
        json_resp["result"] = true;
        json_resp["reason"] = "游戏大厅长连接建立成功";
        std::string body;
        Json_Util::serializa(json_resp, body);
        conn->send(body);
        // 设置session时间为永久
        _sm.set_session_expire_time(sp->get_ssid(), FOREVER);
    }

    // 游戏房间长连接建立
    void wsopen_game_room(wsserver_t::connection_ptr conn)
    {
        // 获取session信息
        session_ptr sp = get_session_comm(conn);
        if(sp.get() == nullptr)
        {
            return;
        }
        // 判断是否重复登录
        Json::Value err_resp;
        if(_om.is_in_game_hall(sp->get_user()) && _om.is_in_game_room(sp->get_user()))
        {
            err_resp["optype"] = "room_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "客户端重复登录";
            std::string body;
            Json_Util::serializa(err_resp, body);
            conn->send(body);
            return;
        }
        // 判断是否已经创建了房间, 获取房间信息
        room_ptr rp = _rm.get_room_by_uid(sp->get_user());
        if(rp.get() == nullptr)
        {
            err_resp["optype"] = "room_ready";
            err_resp["result"] = false;
            err_resp["reason"] = "游戏房间未创建";
            std::string body;
            Json_Util::serializa(err_resp, body);
            conn->send(body);
            return;
        }
        // 长连接加入游戏房间
        _om.enter_game_room(sp->get_user(), conn);
        // 重新设置session时间为永久
        _sm.set_session_expire_time(sp->get_ssid(), FOREVER);
        // 发送响应信息
        Json::Value json_resp;
        json_resp["optype"] = "room_ready";
        json_resp["result"] = true;
        json_resp["room_id"] = (Json::Int64)rp->getId();
        json_resp["uid"] = (Json::Int64)sp->get_user();
        json_resp["white_id"] = (Json::Int64)rp->getWid();
        json_resp["black_id"] = (Json::Int64)rp->getBid();
        std::string body;
        Json_Util::serializa(json_resp, body);
        conn->send(body);
        return;
    }

    // 长连接建立回调处理函数
    void wsopen_callback(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall")
        {
            wsopen_game_hall(conn);
        }
        else if(uri == "/room")
        {
            wsopen_game_room(conn);
        }
    }

    // 游戏大厅长连接断开
    void wclose_game_hall(wsserver_t::connection_ptr conn)
    {
        Json::Value err_resp;
        // 获取请求信息中的cookie，如果没有就要重新登录
        session_ptr sp = get_session_comm(conn);
        if(sp.get() == nullptr)
        {
            return;
        }
        // 移除会话 - 首先要获取session信息
        _om.exit_game_hall(sp->get_user());
        // 重置session时间, 定时删除
        _sm.set_session_expire_time(sp->get_ssid(), FIXTIME);
    }
    // 游戏房间长连接断开
    void wclose_game_room(wsserver_t::connection_ptr conn)
    {
        // 获取session信息
        session_ptr sp = get_session_comm(conn);
        if(sp.get() == nullptr)
        {
            return;
        }
        // 移除游戏房间在线管理
        _om.exit_game_room(sp->get_user());
        // 重置session时间为定时删除
        _sm.set_session_expire_time(sp->get_ssid(), FIXTIME);
        // 玩家退出房间，如果所有玩家退出房间则销毁房间
        _rm.remove_room_user(sp->get_user());
    }
    // 长连接断开回调处理函数
    void wsclose_callback(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall")
        {
            wclose_game_hall(conn);
        }
        else if(uri == "/room")
        {
            wclose_game_room(conn);
        }
    }

    // 游戏大厅长连接通信
    void wmsg_game_hall(wsserver_t::connection_ptr conn, wsserver_t::message_ptr msg)
    {
        // 验证登录信息-获取session
        session_ptr sp = get_session_comm(conn);
        if(sp.get() == nullptr)
        {
            return;
        }
        // 获取请求信息
        Json::Value json_resp;
        std::string body_resp;
        std::string body_req = msg->get_payload();
        Json::Value json_req;
        bool ret = Json_Util::unserializa(body_req, json_req);
        if(!ret)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "请求解析失败";
            Json_Util::serializa(json_resp, body_resp);
            conn->send(body_resp);
            return;
        }
        // 对请求进行处理-开始匹配/停止匹配
        if(!json_req["optype"].isNull() && json_req["optype"].asString() == "match_start")
        {
            _mt.add(sp->get_user());
            json_resp["optype"] = "match_start";
            json_resp["result"] = true;
            Json_Util::serializa(json_resp, body_resp);
            conn->send(body_resp);
            return;
        }
        else if(!json_req["optype"].isNull() && json_req["optype"].asString() == "match_stop")
        {
            _mt.del(sp->get_user());
            json_resp["optype"] = "match_stop";
            json_resp["result"] = true;
            Json_Util::serializa(json_resp, body_resp);
            conn->send(body_resp);
            return;
        }
        json_resp["optype"] = "unknown";
        json_resp["result"] = false;
        Json_Util::serializa(json_resp, body_resp);
        conn->send(body_resp);
    }
    // 游戏房间长连接通信
    void wmsg_game_room(wsserver_t::connection_ptr conn, wsserver_t::message_ptr msg)
    {
        Json::Value json_resp;
        std::string body_resp;
        // 获取session信息
        session_ptr sp = get_session_comm(conn);
        if(sp.get() == nullptr)
        {
            return;
        }
        // 获取房间信息
        room_ptr rp = _rm.get_room_by_uid(sp->get_user());
        if(rp.get() == nullptr)
        {
            json_resp["optype"] = "uknown";
            json_resp["result"] = false;
            json_resp["reason"] = "游戏房间未创建";
            Json_Util::serializa(json_resp, body_resp);
            conn->send(body_resp);
            return;
        }
        // 对请求进行反序列化
        Json::Value json_req;
        std::string body_req = msg->get_payload();
        bool ret = Json_Util::unserializa(body_req, json_req);
        if(!ret)
        {
            json_resp["optype"] = "uknown";
            json_resp["result"] = false;
            json_resp["reason"] = "请求解析失败";
            Json_Util::serializa(json_resp, body_resp);
            conn->send(body_resp);
            return;
        }
        // 处理请求信息
        rp->handle_request(json_req);
    }
    // 长连接通信消息处理函数
    void wsmsg_callback(websocketpp::connection_hdl hdl, wsserver_t::message_ptr msg)
    {
        wsserver_t::connection_ptr conn = _wsvr.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall")
        {
            wmsg_game_hall(conn, msg);
        }
        else if(uri == "/room")
        {
            wmsg_game_room(conn, msg);
        }
    }
public:
    gobang_server(const std::string& host, const std::string& user, const std::string& passwd,
        const std::string& db, int port)
        :_web_root("./wwwroot/"),_ut(host,user,passwd,db,port),_rm(&_ut, &_om),_sm(&_wsvr),
        _mt(&_ut, &_rm, &_om)
    {
        _wsvr.set_access_channels(websocketpp::log::alevel::none);
        _wsvr.init_asio();
        _wsvr.set_reuse_addr(true);
        _wsvr.set_http_handler(std::bind(&gobang_server::http_callback, this, std::placeholders::_1));
        _wsvr.set_open_handler(std::bind(&gobang_server::wsopen_callback, this, std::placeholders::_1));
        _wsvr.set_close_handler(std::bind(&gobang_server::wsclose_callback, this, std::placeholders::_1));
        _wsvr.set_message_handler(std::bind(&gobang_server::wsmsg_callback, this, std::placeholders::_1, std::placeholders::_2));
    }
    
    void Start()
    {
        _wsvr.listen(8081);
        _wsvr.start_accept();
        _wsvr.run();
    }
};